package redis

import (
	"encoding/binary"
	"gitee.com/Cauchy_AQ/gokv"
	"gitee.com/Cauchy_AQ/gokv/utils"
)

// ================================ Set 结构 =============================== //

func (r *RedisDataStruct) SAdd(key []byte, members ...[]byte) (int, error) {
	// 查找并创建元数据
	meta, err := r.findAndCreateMetadata(key, Set)
	if err != nil {
		return 0, err
	}

	uniqueMems := utils.UniqueByteArray(members)

	memberInternalKeys := make([]setInternalKey, len(uniqueMems))
	for i, member := range uniqueMems {
		memberInternalKeys[i] = setInternalKey{
			key:     key,
			version: meta.version,
			member:  member,
		}
	}

	writeBatchOpts := gokv.DefaultWriteBatchOptions
	wb := r.db.NewWriteBatch(&writeBatchOpts)

	cnt := 0
	for _, memberInternalKey := range memberInternalKeys {
		encKey := memberInternalKey.encode()
		if _, err := r.db.Get(encKey); err == gokv.ErrKeyNotFound {
			meta.size += 1
			cnt++
			_ = wb.Put(encKey, nil)
		}
	}
	_ = wb.Put(key, meta.encode())

	if err := wb.Commit(); err != nil {
		return 0, err
	}
	return cnt, nil
}

func (r *RedisDataStruct) SRem(key []byte, members ...[]byte) (int, error) {
	meta, exist, err := r.findMetadata(key, Set)
	if err != nil {
		return 0, err
	}
	if !exist {
		return 0, nil
	}

	// 对 members 去重处理
	uniqueMems := utils.UniqueByteArray(members)

	writeBatchOpts := gokv.DefaultWriteBatchOptions
	wb := r.db.NewWriteBatch(&writeBatchOpts)

	cnt := 0
	for _, member := range uniqueMems {
		internalKey := setInternalKey{
			key:     key,
			version: meta.version,
			member:  member,
		}
		encKey := internalKey.encode()

		var exists bool = true
		if _, err := r.db.Get(encKey); err == gokv.ErrKeyNotFound {
			exists = false
		}
		if exists {
			_ = wb.Delete(encKey)
			meta.size -= 1
			cnt++

			if meta.size == 0 {
				_ = wb.Delete(key)
			}
		}
	}
	_ = wb.Put(key, meta.encode())

	if err := wb.Commit(); err != nil {
		return 0, err
	}
	return cnt, nil
}

func (r *RedisDataStruct) SMembers(key []byte) ([][]byte, error) {
	meta, exist, err := r.findMetadata(key, Set)
	if err != nil {
		return nil, err
	}
	if !exist {
		return nil, nil
	}

	internalKey := setInternalKey{
		key:     key,
		version: meta.version,
	}

	iterOpts := gokv.DefaultIteratorOptions
	iterOpts.Prefix = internalKey.prefix()
	lenPrefix := len(iterOpts.Prefix)

	iter := r.db.NewIterator(&iterOpts)
	defer iter.Close()

	res := make([][]byte, 0)
	for iter.Seek(iterOpts.Prefix); iter.Valid(); iter.Next() {
		res = append(res, iter.Key()[lenPrefix:])
	}

	return res, nil
}

func (r *RedisDataStruct) SIsMember(key, member []byte) bool {
	meta, exist, err := r.findMetadata(key, Set)
	if err != nil || !exist || meta.size == 0 {
		return false
	}

	internalKey := setInternalKey{
		key:     key,
		version: meta.version,
		member:  member,
	}
	encKey := internalKey.encode()
	if _, err := r.db.Get(encKey); err == gokv.ErrKeyNotFound {
		return false
	}
	return true
}

func (r *RedisDataStruct) SCard(key []byte) (int, error) {
	meta, exist, err := r.findMetadata(key, Set)
	if err != nil {
		return 0, err
	}
	if !exist {
		return 0, nil
	}
	return int(meta.size), nil
}

// setInternalKey set 编码结构 key 值
type setInternalKey struct {
	key     []byte
	version int64
	member  []byte
}

// encode setInternalKey 编码
// key|version|member -> null
func (s *setInternalKey) encode() []byte {
	lenk, lenm := len(s.key), len(s.member)
	buf := make([]byte, lenk+lenm+8)
	copy(buf[:lenk], s.key)
	binary.LittleEndian.PutUint64(buf[lenk:lenk+8], uint64(s.version))
	copy(buf[lenk+8:lenk+8+lenm], s.member)
	return buf
}

func (s *setInternalKey) prefix() []byte {
	lenk := len(s.key)
	buf := make([]byte, lenk+8)
	copy(buf[:lenk], s.key)
	binary.LittleEndian.PutUint64(buf[lenk:lenk+8], uint64(s.version))
	return buf
}
